package javango.api;

import com.google.inject.Module;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javango.core.contextprocessors.ContextProcessor;
import javango.middleware.Middleware;
import javango.spi.JavangoModule;

/**
 * Base class for configuring javango framework. Extend this class and initialize it
 * in constructor via {@link Settings#setUrlsClass(java.lang.Class)}, 
 * {@link Settings#setMiddlewareClasses(java.lang.Class<? extends javango.middleware.Middleware>[])} and
 * {@link Settings#setContextProcessors(java.lang.Class<? extends javango.core.contextprocessors.ContextProcessor>[])}.
 * 
 */
public class Settings {
    
    private Urls urlsClass;
    private List<Class<? extends Middleware>> middlewareClasses;
    private List<Class<? extends ContextProcessor>> contextProcessorClasses;
    private List<Class<? extends Module>> moduleClasses;
    private List<JavangoModule> javangoModules;
    private boolean debug;
    private Map<String, String> localMap = new HashMap<String, String>();

    final public Urls getUrls() {
        return urlsClass;
    }

    final public void setUrlsClass(Class<? extends Urls> urlsClass) {
        if (this.urlsClass != null) {
            throw new IllegalStateException("urlsClass already initialized - cannot be called twice");
        }
        try {
            this.urlsClass = urlsClass.newInstance();
        } catch (InstantiationException ex) {
            Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    final public void setMiddlewareClasses(Class<? extends Middleware> ...middlewareClassesArray) {
        if (this.middlewareClasses != null) {
            throw new IllegalStateException("middleware already initialized - cannot be called twice");
        }
        middlewareClasses = Arrays.asList(middlewareClassesArray);
    }

    public List<Class<? extends Middleware>> getMiddlewareClasses() {
        if (middlewareClasses == null) {
            return new ArrayList<Class<? extends Middleware>>();
        } else {
            return Collections.unmodifiableList(middlewareClasses);
        }
    }

    final public List<Class<? extends ContextProcessor>> getContextProcessors() {
        if (contextProcessorClasses == null) {
            return new ArrayList<Class<? extends ContextProcessor>>();
        } else {
            return Collections.unmodifiableList(contextProcessorClasses);
        }
    }

    final public void setContextProcessorClasses(Class<? extends ContextProcessor> ...contextProcessorsArray) {
        if (this.contextProcessorClasses != null) {
            throw new IllegalStateException("contextProcessors already initialized - cannot be called twice");
        }
        contextProcessorClasses = Arrays.asList(contextProcessorsArray);
    }

    final public List<Class<? extends Module>> getModuleClasses() {
        return moduleClasses;
    }

    final public void setModuleClasses(Class<? extends Module> ...moduleClassesArray) {
        if (this.moduleClasses != null) {
            throw new IllegalStateException("modules already initialized - cannot be called twice");
        }
        moduleClasses = Arrays.asList(moduleClassesArray);
    }

    
    final public boolean isDebug() {
        return debug;
    }

    final public void setDebug(boolean debug) {
        this.debug = debug;
    }

    final public boolean isValid() {
        // TODO: valid if eg. is Urls class was set
        return true;
    }
    
    final public String get(String property) {		
		if (localMap.containsKey(property)) return localMap.get(property);
		return null;
	}
	
    final public void set(String property, String value) {
		localMap.put(property, value);
	}

    final public String get(String property, String defaultValue) {
		String value = get(property);
		if (value == null) return defaultValue;
		return value;
	}

    final public List<JavangoModule> getJavangoModules() {
        if (javangoModules == null) {
            List<JavangoModule> l = new ArrayList<JavangoModule>();
            try {
                l.add((JavangoModule) Class.forName("javango.contrib.hibernate.JavangoModule").newInstance());
                l.add((JavangoModule) Class.forName("javango.contrib.jquery.JavangoModule").newInstance());
                l.add((JavangoModule) Class.forName("javango.forms.JavangoModule").newInstance());
            } catch (InstantiationException ex) {
                Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
            }
            return l;
        } else {
            return javangoModules;
        }
    }
    
    final public void setJavangoModuleClasses(Class<? extends JavangoModule> ...javangoModulesArray) {
        if (this.javangoModules != null) {
            throw new IllegalStateException("javangoModules already initialized - cannot be called twice");
        }
        javangoModules = new ArrayList<JavangoModule>();
        for (Class<? extends JavangoModule> clazz : javangoModulesArray) {
            try {
                javangoModules.add(clazz.newInstance());
            } catch (InstantiationException ex) {
                Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

}
